use trie::trie::Trie;

#[test]
fn test_trie_new_free() {
    // Test allocation and freeing of an empty trie
    let trie = Trie::<&str>::new();
    assert!(trie.root_node.is_none());

    // Test adding values before freeing
    let mut trie = Trie::new();
    assert!(trie.insert_binary(b"hello", Some("there")));
    assert!(trie.insert_binary(b"hell", Some("testing")));
    assert!(trie.insert_binary(b"testing", Some("testing")));
    assert!(trie.insert_binary(b"", Some("asfasf")));

    // Test adding a value, removing it, and then freeing
    let mut trie = Trie::new();
    assert!(trie.insert_binary(b"hello", Some("there")));
    assert!(trie.remove_binary(b"hello"));
}

#[test]
fn test_trie_insert() {
    let num_test_values = 10000;
    let mut test_array = vec![0i32; num_test_values];
    let mut test_strings = vec![String::new(); num_test_values];
    let mut trie = Trie::<i32>::new();

    for i in 0..num_test_values {
        test_array[i] = i as i32;
        test_strings[i] = i.to_string();
        assert!(trie.insert_binary(test_strings[i].as_bytes(), Some(test_array[i])));
        assert_eq!(trie.num_entries(), i + 1);
    }

    // Test insert of NULL value has no effect
    let entries = trie.num_entries();
    assert!(!trie.insert_binary(b"hello world", None));
    assert_eq!(trie.num_entries(), entries);
}

#[test]
fn test_trie_lookup() {
    let mut trie = Trie::<i32>::new();
    let num_test_values = 10000;
    let mut test_array = vec![0i32; num_test_values];
    let mut test_strings = vec![String::new(); num_test_values];

    for i in 0..num_test_values {
        test_array[i] = i as i32;
        test_strings[i] = i.to_string();
        assert!(trie.insert_binary(test_strings[i].as_bytes(), Some(test_array[i])));
    }

    // Test lookup for non-existent values
    assert!(trie.lookup_binary(b"000000000000000").is_none());
    assert!(trie.lookup_binary(b"").is_none());

    // Look up all values
    for i in 0..num_test_values {
        let buf = i.to_string();
        let val = trie.lookup_binary(buf.as_bytes());
        assert_eq!(val, Some(i as i32));
    }
}

#[test]
fn test_trie_remove() {
    let mut trie = Trie::<i32>::new();
    let num_test_values = 10000;
    let mut test_array = vec![0i32; num_test_values];
    let mut test_strings = vec![String::new(); num_test_values];

    for i in 0..num_test_values {
        test_array[i] = i as i32;
        test_strings[i] = i.to_string();
        assert!(trie.insert_binary(test_strings[i].as_bytes(), Some(test_array[i])));
    }

    // Test remove on non-existent values
    assert!(!trie.remove_binary(b"000000000000000"));
    assert!(!trie.remove_binary(b""));

    let mut entries = trie.num_entries();
    assert_eq!(entries, num_test_values);

    // Remove all values
    for i in 0..num_test_values {
        let buf = i.to_string();
        assert!(trie.remove_binary(buf.as_bytes()));
        entries -= 1;
        assert_eq!(trie.num_entries(), entries);
    }
}

#[test]
fn test_trie_replace() {
    let mut trie = Trie::<i32>::new();
    let num_test_values = 10000;
    let mut test_array = vec![0i32; num_test_values];
    let mut test_strings = vec![String::new(); num_test_values];

    for i in 0..num_test_values {
        test_array[i] = i as i32;
        test_strings[i] = i.to_string();
        assert!(trie.insert_binary(test_strings[i].as_bytes(), Some(test_array[i])));
    }

    // Test replacing values
    let val = 999;
    assert!(trie.insert_binary(b"999", Some(val)));
    assert_eq!(trie.num_entries(), num_test_values);
    assert_eq!(trie.lookup_binary(b"999"), Some(val));
}

#[test]
fn test_trie_insert_empty() {
    let mut trie = Trie::<String>::new();

    // Test insert on empty string
    let buf = "test".to_string();
    assert!(trie.insert_binary(b"", Some(buf.clone())));
    assert_ne!(trie.num_entries(), 0);
    assert_eq!(trie.lookup_binary(b""), Some(buf.clone()));
    assert!(trie.remove_binary(b""));
    assert_eq!(trie.num_entries(), 0);
}

#[test]
fn test_trie_free_long() {
    let mut trie = Trie::<String>::new();
    let long_string = "A".repeat(4095) + "\0";

    // Create a trie and add the string
    assert!(trie.insert_binary(long_string.as_bytes(), Some(long_string.clone())));
}

#[test]
fn test_trie_negative_keys() {
    let mut trie = Trie::<&str>::new();
    let my_key = &[b'a', b'b', b'c', 206, 236, 0];

    assert!(trie.insert_binary(my_key, Some("hello world")));

    let value = trie.lookup_binary(my_key);
    assert_eq!(value, Some("hello world"));

    assert!(trie.remove_binary(my_key));
    assert!(!trie.remove_binary(my_key));
    assert!(trie.lookup_binary(my_key).is_none());
}

#[test]
fn test_trie_insert_binary() {
    let mut trie = Trie::<&str>::new();
    let bin_key = &[b'a', b'b', b'c', 0, 1, 2, 0xff];
    let bin_key2 = &[b'a', b'b', b'c', 0, 1, 2, 0xff, 0];
    let bin_key3 = &[b'a', b'b', b'c'];

    // Insert some values
    assert!(trie.insert_binary(bin_key2, Some("goodbye world")));
    assert!(trie.insert_binary(bin_key, Some("hello world")));

    // Overwrite a value
    assert!(trie.insert_binary(bin_key, Some("hi world")));

    // Insert NULL value doesn't work
    assert!(!trie.insert_binary(bin_key3, None));

    // Read them back
    let value = trie.lookup_binary(bin_key);
    assert_eq!(value, Some("hi world"));

    let value = trie.lookup_binary(bin_key2);
    assert_eq!(value, Some("goodbye world"));
}

#[test]
fn test_trie_remove_binary() {
    let mut trie = Trie::<&str>::new();
    let bin_key = &[b'a', b'b', b'c', 0, 1, 2, 0xff];
    let bin_key2 = &[b'a', b'b', b'c', 0, 1, 2, 0xff, 0];
    let bin_key3 = &[b'a', b'b', b'c'];
    let bin_key4 = &[b'z', 0, b'z', b'z'];

    // Insert some values
    assert!(trie.insert_binary(bin_key2, Some("goodbye world")));
    assert!(trie.insert_binary(bin_key, Some("hello world")));

    // Test look up and remove of invalid values
    assert!(trie.lookup_binary(bin_key3).is_none());
    assert!(!trie.remove_binary(bin_key3));

    assert!(trie.lookup_binary(bin_key4).is_none());
    assert!(!trie.remove_binary(bin_key4));

    // Remove the two values
    assert!(trie.remove_binary(bin_key2));
    assert!(trie.lookup_binary(bin_key2).is_none());
    assert!(trie.lookup_binary(bin_key).is_some());

    assert!(trie.remove_binary(bin_key));
    assert!(trie.lookup_binary(bin_key).is_none());
}